#region

using System;
using System.Data.Entity;
using System.Data.Entity.Validation;
using System.Linq;
using HouseHoldBudget.Core;
using HouseHoldBudget.Core.Data;

#endregion

namespace HouseHoldBudget.Data
{
    /// <summary>
    ///   Entity Framework repository
    /// </summary>
    public class EfRepository<T> : IRepository<T> where T : BaseEntity
    {
        #region Readonly & Static Fields

        private readonly IDbContext _context;

        #endregion

        #region Fields

        private IDbSet<T> _entities;

        #endregion

        #region Constructors

        /// <summary>
        ///   Ctor
        /// </summary>
        /// <param name="context"> Object context </param>
        public EfRepository(IDbContext context)
        {
            _context = context;
        }

        #endregion

        #region Instance Properties

        /// <summary>
        ///   Gets entities.
        /// </summary>
        /// <value> The entities. </value>
        private IDbSet<T> Entities
        {
            get
            {
                if (_entities == null)
                    _entities = _context.Set<T>();
                return _entities;
            }
        }

        #endregion

        #region IRepository<T> Members

        /// <summary>
        ///   Gets an entity by id.
        /// </summary>
        /// <param name="id"> The id. </param>
        /// <returns> </returns>
        public T GetById(object id)
        {
            return Entities.Find(id);
        }

        /// <summary>
        ///   Inserts the specified entity.
        /// </summary>
        /// <param name="entity"> The entity. </param>
        /// <exception cref="System.ArgumentNullException">entity</exception>
        public void Insert(T entity)
        {
            try
            {
                if (entity == null)
                    throw new ArgumentNullException("entity");

                Entities.Add(entity);

                _context.SaveChanges();
            }
            catch (DbEntityValidationException dbEx)
            {
                var msg = string.Empty;

                foreach (var validationErrors in dbEx.EntityValidationErrors)
                    foreach (var validationError in validationErrors.ValidationErrors)
                        msg += string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage) + Environment.NewLine;

                var fail = new Exception(msg, dbEx);
                //Debug.WriteLine(fail.Message, fail);
                throw fail;
            }
        }

        /// <summary>
        ///   Updates the specified entity.
        /// </summary>
        /// <param name="entity"> The entity. </param>
        /// <exception cref="System.ArgumentNullException">entity</exception>
        public void Update(T entity)
        {
            try
            {
                if (entity == null)
                    throw new ArgumentNullException("entity");

                _context.SaveChanges();
            }
            catch (DbEntityValidationException dbEx)
            {
                var msg = string.Empty;

                foreach (var validationErrors in dbEx.EntityValidationErrors)
                    foreach (var validationError in validationErrors.ValidationErrors)
                        msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);

                var fail = new Exception(msg, dbEx);
                //Debug.WriteLine(fail.Message, fail);
                throw fail;
            }
        }

        /// <summary>
        ///   Deletes an entity by id.
        /// </summary>
        /// <param name="id"> The id. </param>
        /// <exception cref="System.Exception">Could not find entity with ID: + id.ToString()</exception>
        public void DeleteById(object id)
        {
            var entity = GetById(id);
            if (entity == null)
                throw new Exception("Could not find entity with ID:" + id);

            Delete(entity);
        }

        /// <summary>
        ///   Deletes the specified entity.
        /// </summary>
        /// <param name="entity"> The entity. </param>
        /// <exception cref="System.ArgumentNullException">entity</exception>
        public void Delete(T entity)
        {
            try
            {
                if (entity == null)
                    throw new ArgumentNullException("entity");

                Entities.Remove(entity);

                _context.SaveChanges();
            }
            catch (DbEntityValidationException dbEx)
            {
                var msg = string.Empty;

                foreach (var validationErrors in dbEx.EntityValidationErrors)
                    foreach (var validationError in validationErrors.ValidationErrors)
                        msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);

                var fail = new Exception(msg, dbEx);
                //Debug.WriteLine(fail.Message, fail);
                throw fail;
            }
        }

        /// <summary>
        ///   Gets the entity table.
        /// </summary>
        /// <value> The table. </value>
        public virtual IQueryable<T> Table
        {
            get
            {
                return Entities;
            }
        }

        #endregion
    }
}